package com.katze.common.tree;

import java.io.Serial;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 说  明：基础的树形节点对象
 * 作  者：zf.zeng
 * 日  期：2020-12-03 (星期四)
 **/
public class Node implements Serializable {

    @Serial
    private static final long serialVersionUID = -6808484353403308852L;

    private Serializable id;

    private String name;

    private Serializable pid;

    private String icon;

    private String path;

    private Integer order = 0;

    private byte[] data;

    private List<? super Node> children;

    private Date createTime;

    private Date modifyTime;

    private boolean disable = false;

    private boolean visible = true;

    public Node() {}

    public Node(Serializable id, Serializable pid, String name) {
        this.id = id;
        this.name = name;
        this.pid = pid;
    }

    public Integer getComparator() {
        return getOrder() == null ? -1 : getOrder();
    }

    public void addChildren(Node node) {
        if (this.children == null) this.children = new LinkedList<>();
        this.children.add(node);
    }

    public void addChildren(List<? extends Node> node) {
        if (!node.isEmpty()) {
            if (this.children == null) this.children = new LinkedList<>();
            node.sort((o1, o2) -> o1.getComparator().equals(o2.getComparator()) ?o1.getCreateTime().compareTo(o2.getCreateTime()):o1.getComparator() - o2.getComparator());
            this.children.addAll(node);
        }
    }

    public String getLabel() {
        return getName();
    }

    public Serializable getValue() {
        return getId();
    }

    public static <T extends Node>List<T> build(List<T> nodes) {
        Map<Serializable, List<T>> map = nodes.stream().collect(Collectors.groupingBy(Node::getPid));
        List<T> root = map.remove("0");
        if (root != null){
            root.sort((o1, o2) -> o1.getComparator().equals(o2.getComparator()) ?o1.getCreateTime().compareTo(o2.getCreateTime()):o1.getComparator() - o2.getComparator());
            toTreeView(root, map);
        }
        if (!map.isEmpty()) {
            Set<Serializable> keys =  new HashSet<>(map.keySet());
            if (root == null) root= new ArrayList<>();
            for (Serializable key : keys) {
                if (map.containsKey(key)) {
                    List<T> temp = map.remove(key);
                    toTreeView(temp, map);
                    root.addAll(temp);
                }
            }
            root.sort((o1, o2) -> o1.getComparator().equals(o2.getComparator()) ?o1.getCreateTime().compareTo(o2.getCreateTime()):o1.getComparator() - o2.getComparator());
        }
        return root;
    }

    private static void toTreeView(List<? extends Node> nodes, Map<Serializable, ? extends List<? extends Node>> map) {
        nodes.forEach(node -> {
            List<? extends Node> children = map.remove(node.getId());
            if (children == null) return;
            children.sort((o1, o2) -> o1.getComparator().equals(o2.getComparator()) ?o1.getCreateTime().compareTo(o2.getCreateTime()):o1.getComparator() - o2.getComparator());
            node.addChildren(children);
            toTreeView(children, map);
        });
    }

    public Serializable getId() {
        return id;
    }

    public void setId(Serializable id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Serializable getPid() {
        return pid;
    }

    public void setPid(Serializable pid) {
        this.pid = pid;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Integer getOrder() {
        return order;
    }

    public void setOrder(Integer order) {
        this.order = order;
    }

    public String getData() {
        return data == null || data.length == 0? null:new String(this.data);
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public List<? super Node> getChildren() {
        return children;
    }

    public void setChildren(List<? super Node> children) {
        this.children = children;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getModifyTime() {
        return modifyTime;
    }

    public void setModifyTime(Date modifyTime) {
        this.modifyTime = modifyTime;
    }

    public boolean isDisable() {
        return disable;
    }

    public void setDisable(boolean disable) {
        this.disable = disable;
    }

    public boolean isVisible() {
        return visible;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    @Override
    public String toString() {
        return "{"
                + "\"id\":" + this.getId() + ","
                + "\"label\":" + this.getName() + ","
                + "\"parent\":" + this.getPid() + ","
                + "\"path\":" + this.getPath() + ","
                + "\"data\":" + this.getData() + ","
                + "\"children\":" + (this.getChildren() != null ? this.getChildren().size() : null)
                + "}";
    }
}
